`include "mycpu.h"

module csr (
    input clk,
    input  reset ,
    input [`WS_TO_CSR_BUS_WD-1:0] ws_to_csr_bus,
    output [63:0] csr_rdata,
    output [63:0] csr_flush_pc,
    output        csr_flush_o,
    output        csr_MIE,
    output        csr_MTIE,
    ////diff_test////
    output [3:0]  diff_intrNO,
    output [3:0]  diff_cause,
    output [63:0] diff_mstatus,
    output [63:0] diff_mepc,
    output [63:0] diff_mtvec,
    output [63:0] diff_mcause,
    output [63:0] diff_mip,
    output [63:0] diff_mie        
);

reg [1:0]   csr_mstatus_MPP;
reg         csr_mstatus_MPIE;
reg         csr_mstatus_MIE;
reg         csr_mstatus_SD;
reg  [1:0]  csr_mstatus_XS;
reg  [1:0]  csr_mstatus_FS;
reg [1:0]   csr_mtvec_MODE;
reg [61:0]  csr_mtvec_BASE;
reg [63:0]  csr_mepc;
reg         csr_mcause_int;
reg [62:0]  csr_mcause_excode;
wire [63:0] csr_mip;
reg  [63:0] csr_mcycle;
reg         csr_mie_MTIE;


wire[11:0] csr_addr;
wire[4:0]   ws_zimm;
wire        ws_int;
wire        ws_ex;
wire  ws_valid;
wire  op_csrrw ; 
wire  op_csrrs ;
wire  op_csrrc ;
wire  op_csrrwi;
wire  op_csrrsi;
wire  op_csrrci;
wire  op_mret;
wire [63:0] csr_wdata;
wire [63:0] ws_pc;
wire [3:0] excode;
reg tick;
wire [63:0] csr_mstatus;
wire [63:0] csr_mtvec;
// wire [63:0] csr_mepc;
wire [63:0] csr_mcause;
// wire [63:0] csr_mip;
wire [63:0] csr_mie;
wire [63:0] ws_rs1_value;

////we==>write_enable////
wire    mtvec_we;
wire    mepc_we;
wire    mstatus_we;
wire    mcause_we;
wire    mcycle_we;
wire    mie_we;
////se==>set_enable////
wire    mtvec_se;
wire    mepc_se;
wire    mstatus_se;
wire    mcause_se;
wire    mcycle_se;
wire    mie_se;
////ce==>set_enable////
wire    mtvec_ce;
wire    mepc_ce;
wire    mstatus_ce;
wire    mcause_ce;
wire    mcycle_ce;
wire    mie_ce;
//
wire    csrrwe;
wire    csrrse;
wire    csrrce;
//
wire    int_we;
wire   is_mstatus;
wire   is_mtvec;
wire   is_mepc;
wire   is_mcause;
wire   is_mip;
wire   is_mie;
wire   is_mcycle;

assign csrrwe      = ws_valid && (op_csrrw || op_csrrwi);
assign csrrse      = ws_valid && (op_csrrs || op_csrrsi) && ws_zimm != 5'h0;
assign csrrce      = ws_valid && (op_csrrc || op_csrrci) && ws_zimm != 5'h0;

assign csr_mstatus = {csr_mstatus_SD, 46'b0,csr_mstatus_XS,csr_mstatus_FS,csr_mstatus_MPP,3'b0,csr_mstatus_MPIE,3'b0,csr_mstatus_MIE,3'b0};
assign csr_mtvec   = {csr_mtvec_BASE,2'b0};
assign csr_mcause  = {csr_mcause_int,csr_mcause_excode};
assign csr_mip     = 64'b0;
assign csr_mie     = {56'b0,csr_mie_MTIE,7'b0};

assign is_mstatus  = csr_addr == 12'h300;
assign is_mie      = csr_addr == 12'h304;
assign is_mtvec    = csr_addr == 12'h305;
assign is_mepc     = csr_addr == 12'h341;
assign is_mcause   = csr_addr == 12'h342;
assign is_mip      = csr_addr == 12'h344;
assign is_mcycle   = csr_addr == 12'hB00;


assign mstatus_we = csrrwe && is_mstatus;
assign mtvec_we   = csrrwe && is_mtvec;
assign mepc_we    = csrrwe && is_mepc;
assign mie_we     = csrrwe && is_mie;
assign mcause_we  = csrrwe && is_mcause;
assign mcycle_we  = csrrwe && is_mcycle;

assign mstatus_se = csrrse && is_mstatus;
assign mtvec_se   = csrrse && is_mtvec;
assign mepc_se    = csrrse && is_mepc;
assign mie_se     = csrrse && is_mie;
assign mcause_se  = csrrse && is_mcause;
assign mcycle_se  = csrrse && is_mcycle;

assign mstatus_ce = csrrce && is_mstatus;
assign mtvec_ce   = csrrce && is_mtvec;
assign mepc_ce    = csrrce && is_mepc;
assign mie_ce     = csrrce && is_mie;
assign mcause_ce  = csrrce && is_mcause;
assign mcycle_ce  = csrrce && is_mcycle;



assign excode     = ws_int ? 4'd07 :4'd11;

assign csr_rdata  = ({64{is_mstatus  } }  & csr_mstatus )
                  | ({64{is_mie      }}   & csr_mie )
                  | ({64{is_mtvec    }}   & csr_mtvec )
                  | ({64{is_mepc     }}   & csr_mepc )
                  | ({64{is_mcause   }}   & csr_mcause )
                  | ({64{is_mip      }}   & csr_mip )
                  | ({64{is_mcycle   }}   & csr_mcycle );
assign csr_flush_pc = op_mret && ws_valid ? csr_mepc : csr_mtvec;
assign csr_flush_o  = ( op_mret || ws_ex || op_mret ) && ws_valid;



always @(posedge clk) begin
    ////MIE////
    if (reset) begin
        csr_mstatus_MIE  <= 1'b0;
    end
    else if (mstatus_we ) begin
        csr_mstatus_MIE  <= csr_wdata[3];
    end
    else if (mstatus_se) begin
        csr_mstatus_MIE <= csr_wdata[3] | csr_mstatus_MIE;
    end
    else if (mstatus_ce ) begin
        csr_mstatus_MIE  <= csr_wdata[3] ? 1'b0 : csr_mstatus_MIE;
    end
    else if (ws_ex && ws_valid) begin
        csr_mstatus_MIE  <= 1'b0;
    end
    else if (op_mret && ws_valid) begin
        csr_mstatus_MIE  <=  csr_mstatus_MPIE;
    end
    ////MPIE////
    if (reset ) begin
        csr_mstatus_MPIE <= 1'b0;
    end
    else if (mstatus_we ) begin
        csr_mstatus_MPIE <= csr_wdata[7];
    end
    else if (mstatus_se ) begin
        csr_mstatus_MPIE <= csr_wdata[7] | csr_mstatus_MPIE;
    end
    else if (mstatus_ce  ) begin
        csr_mstatus_MPIE <= csr_wdata[7] ? 1'b0 : csr_mstatus_MPIE;
    end
    else if (ws_ex && ws_valid ) begin
        csr_mstatus_MPIE <= csr_mstatus_MIE;
    end
    else if (op_mret && ws_valid) begin
        csr_mstatus_MPIE <= 1'b1;
    end
    ////MPP////
    if (reset )begin
        csr_mstatus_MPP <= 2'h0;
    end
    else if (mstatus_we ) begin
        csr_mstatus_MPP <= csr_wdata[12:11];
    end
    else if (mstatus_se ) begin
        csr_mstatus_MPP <= csr_wdata[12:11] | csr_mstatus_MPP;
    end
    else if (mstatus_ce ) begin
        csr_mstatus_MPP <= ~csr_wdata[12:11] & csr_mstatus_MPP;
    end
    else if (ws_ex && ws_valid) begin
        csr_mstatus_MPP <= 2'h3;
    end
    else if (op_mret && ws_valid) begin
        csr_mstatus_MPP <= 2'h3;
    end
    ////XS////
    if (reset) begin
        csr_mstatus_XS <= 2'b0;
    end
    else if (mstatus_we ) begin
        csr_mstatus_XS <= csr_wdata[16:15];
    end
    else if (mstatus_se) begin
        csr_mstatus_XS <= csr_wdata[16:15] | csr_mstatus_XS;
    end
    else if (mstatus_ce) begin
        csr_mstatus_XS <= ~csr_wdata[16:15] & csr_mstatus_XS;
    end
    ////FS////
    if (reset) begin
        csr_mstatus_FS <= 2'b0;
    end
    else if (mstatus_we ) begin
        csr_mstatus_FS <= csr_wdata[14:13];
    end
     else if (mstatus_se) begin
        csr_mstatus_FS <=  csr_wdata[14:13] | csr_mstatus_FS;
    end
    else if (mstatus_ce) begin
        csr_mstatus_FS <= ~csr_wdata[14:13] & csr_mstatus_FS;
    end
    ////SD////
    if (reset  ) begin
        csr_mstatus_SD <= 1'b0;
    end
    else if (mstatus_we ) begin
        csr_mstatus_SD <= csr_wdata[14:13] == 2'b11 || csr_wdata[16:15] == 2'b11;
    end
    else if (mstatus_se ) begin
       csr_mstatus_SD <= ( csr_wdata[14:13] | csr_mstatus_FS) == 2'b11 || ( csr_wdata[16:15] | csr_mstatus_XS);
    end
    else if (mstatus_ce ) begin
       csr_mstatus_SD <= (~csr_wdata[14:13] & csr_mstatus_FS) == 2'b11 || (~csr_wdata[16:15] & csr_mstatus_XS);
    end
    else if ( csr_mstatus_FS != 2'b11 && csr_mstatus_XS != 2'b11) begin
        csr_mstatus_SD <= 1'b0;
    end
end

always @(posedge clk) begin 
    ////MTVEC_BASE////
     if (mtvec_we ) begin
        csr_mtvec_BASE <= csr_wdata[63:2];
    end
   else if (mtvec_se ) begin
        csr_mtvec_BASE <= csr_wdata[63:2] | csr_mtvec_BASE;
    end 
    else if (mtvec_ce ) begin
        csr_mtvec_BASE <= ~csr_wdata[63:2] & csr_mtvec_BASE;
    end
end

always @(posedge clk) begin
    //////mepc//////
   if (mepc_we ) begin
        csr_mepc    <= csr_wdata;
    end
    else if (mepc_se ) begin
        csr_mepc    <= csr_wdata | csr_mepc;
    end
    else if (mepc_ce ) begin
        csr_mepc    <= ~csr_wdata & csr_mepc;
    end
    else if (ws_ex && ws_valid) begin
        csr_mepc    <= ws_pc;
    end   
    
end
////int////
assign int_we = ws_valid && ws_int;
always @(posedge clk) begin
    if (reset) begin
        csr_mcause_int <= 1'b0;
    end
    else if (int_we) begin
        csr_mcause_int <= 1'b1;
    end
    else if (ws_ex && !ws_int && ws_valid ) begin
        csr_mcause_int <= 1'b0;
    end
    // else if (op_mret && ws_valid) begin
    //     csr_mcause_int <= 1'b0;
    // end
    ////mcause_codew////
    if (reset) begin
        csr_mcause_excode <= 63'b0;
    end
    else if (mcause_we ) begin
        csr_mcause_excode <= csr_wdata[63:1];
    end
     else if (mcause_se ) begin
        csr_mcause_excode <= csr_wdata[63:1] | csr_mcause_excode;
    end
     else if (mcause_we ) begin
        csr_mcause_excode <= ~csr_wdata[63:1] & csr_mcause_excode;
    end
    else if (ws_ex && ws_valid) begin
        csr_mcause_excode <= {59'b0,excode};
    end
end
////mie_MTIE////

always @(posedge clk) begin
    if (reset )begin
        csr_mie_MTIE <= 1'b0;
    end
    else if (mie_we) begin
        csr_mie_MTIE <= csr_wdata[7];
    end
    else if (mie_se ) begin
        csr_mie_MTIE <=  csr_wdata[7] | csr_mie_MTIE;
    end
    else if (mie_ce ) begin
        csr_mie_MTIE <= ~csr_wdata[7] & csr_mie_MTIE;
    end
end
assign csr_MIE  = csr_mstatus_MIE;
assign csr_MTIE = csr_mie_MTIE;
////mcycle//
always @(posedge clk) begin
    if (reset) tick <= 1'b0;
        else tick <= ~tick;
    if (mcycle_we ) begin
        csr_mcycle <= csr_wdata;
    end
    else if (mcycle_se ) begin
        csr_mcycle <=  csr_wdata | csr_mcycle;
    end
    else if (mcycle_ce ) begin
        csr_mcycle <= ~csr_wdata & csr_mcycle;
    end
    else  begin
        csr_mcycle <= csr_mcycle + 1'b1;
    end
end



assign csr_wdata = (op_csrrci || op_csrrsi || op_csrrwi) ? {59'b0,ws_zimm} : ws_rs1_value;
assign {
    ws_int,
    ws_ex,
    ws_valid,
    op_mret,
    op_csrrw ,
    op_csrrs ,
    op_csrrc ,
    op_csrrwi,
    op_csrrsi,
    op_csrrci,                        
    ws_zimm,
    csr_addr,
    ws_pc,
    ws_rs1_value
} = ws_to_csr_bus;

///////////////////////////difftest//////////////////////////////

/////CSR_diff////
assign  diff_mstatus    = ws_ex && ws_valid ?  {csr_mstatus_SD, 46'b0,csr_mstatus_XS,csr_mstatus_FS, 2'd3,3'd0,csr_mstatus_MIE,3'h0,4'b0} :
                          mstatus_we   ? { (csr_wdata[14:13] == 2'b11 || csr_wdata[16:15] == 2'b11 ),csr_wdata[62:0] } :
                          mstatus_se   ? csr_wdata | csr_mstatus :
                          mstatus_ce   ? ~csr_wdata & csr_mstatus :          
                          op_mret && ws_valid ? {csr_mstatus_SD, 46'b0,csr_mstatus_XS,csr_mstatus_FS,2'h3,3'b0,1'b1,3'b0,csr_mstatus_MPIE,3'b0} :
                                                 csr_mstatus;//| {51'b0,2'd3,11'b0}
assign  diff_mepc       = ws_ex && ws_valid ? ws_pc : 
                          mepc_we  ? csr_wdata:
                          mepc_se  ? csr_wdata | csr_mepc : 
                          mepc_ce  ? ~csr_wdata & csr_mepc :  csr_mepc;
assign  diff_mtvec      = mtvec_we ? {csr_wdata[63:2] ,2'b0} : 
                          mtvec_se ? csr_wdata | csr_mtvec :
                          mtvec_ce ? ~csr_wdata & csr_mtvec : csr_mtvec;

assign  diff_mcause     = ws_ex && ws_valid ? {ws_int,59'b0,excode} : 
                         mcause_we ? csr_wdata :
                         mcause_se ? csr_wdata | csr_mcause :
                         mcause_ce ? ~csr_wdata & csr_mcause :  csr_mcause;
assign  diff_mip        = csr_mip ;
assign  diff_mie        = mie_we  ?  csr_wdata :
                          mie_se  ?  csr_wdata | csr_mie :
                          mie_ce  ?   ~csr_wdata & csr_mie : csr_mie ;
//////ARCH_EVENT_diff/////  
assign diff_cause  = ws_ex && !ws_int && ws_valid ? 4'd11 : 4'h0;
assign diff_intrNO = ws_int && ws_valid  ? 4'd7 : 4'h0;
            
///////////////////////////////////////////////////////////////////////////////////////////////




endmodule